#include "MainWindow.h"
#include "ProductsModelDelegate.h"
#include "InvoiceModelDelegate.h"
#include "InvoicesParser.h"
#include "CustomerInputDialog.h"
#include "ShopTreeModelDelegate.h"
#include "InvoiceDialog.h"
#include "Helper.h"
#include "ui_Mainwindow.h"

#include <QFileDialog>
#include <QMessageBox>
#include <QInputDialog>
#include <assert.h>
#include <QTextStream>

using namespace ecocity;

static const QString all_proveiders = "Todos";
static const QString all_customers = "Todos";

//------------------------------------------------------------------------------------------------

MainWindow::MainWindow( const ecocity::Shop& shop, QWidget *parent )
    :
    QMainWindow( parent ),
    m_shop( shop ),
    m_products_model( m_shop.products( ) ),
    m_customers_model( m_shop.customers( ) ),
    m_shop_model( m_shop ),
    m_proveiders( Helper::get_proveider_names( m_shop.products( ) ) ),
    products_filter_model( new QSortFilterProxyModel( this ) ),
    customers_filter_model( new QSortFilterProxyModel( this ) ),
    products_delegate( new ProductsModelDelegate( m_shop.proveiders( ), this ) )
{
    setupUi( this );

    // Set Models first ( see top constructor MainWindow)
    products_filter_model->setSourceModel( &m_products_model );
    customers_filter_model->setSourceModel( &m_customers_model );

    // Set Views
    products_view->setModel( products_filter_model );
    customers_view->setModel( customers_filter_model );
    invoices_tree_view->setModel( &m_shop_model );

    // Set Delegates
    products_view->setItemDelegate( products_delegate );
    invoices_tree_view->setItemDelegate( new ShopTreeModelDelegate );

    update_proveiders_combobox( );

    // Set Customer Type ComboBox
    customer_type_box->addItem( all_customers );
    customer_type_box->addItems( Shop::customer_types( ) );

    // Set Price Types list
    m_price_types <<  Product::to_string( Product::PER_KILO )
                  <<  Product::to_string( Product::PER_UNIT );

    // Detalles
    delete_product->setToolTip( "Eliminar producto seleccionado." );

    products_view->resizeColumnsToContents( );
    products_view->horizontalHeader()->setStretchLastSection( true );
    products_view->setSortingEnabled( true );

    customers_view->resizeColumnsToContents( );
    customers_view->horizontalHeader()->setStretchLastSection( true );
    customers_view->setSortingEnabled( true );

    // By default show products tab
    tabWidget->setCurrentWidget( products_tab );

    // Set Connections
    // Proveider ComboBox changedIndex
    connect( proveider_box, SIGNAL( currentIndexChanged( QString ) ),
             this, SLOT( show_products_from_proveider( QString ) ) );
    // Customer Type ComboBox changedIndex
    connect( customer_type_box, SIGNAL( currentIndexChanged( QString ) ),
             this, SLOT( show_customer_type( QString ) ) );

    connect( products_delegate, SIGNAL( price_changed( QAbstractItemModel*,QModelIndex,float ) ),
             this, SLOT( update_product_price( QAbstractItemModel*,QModelIndex,  float ) ) );

    showMaximized( );
}

//------------------------------------------------------------------------------------------------

void MainWindow::on_actionSave_triggered()
{
    QString file = QFileDialog::getSaveFileName( this, tr( "Guardar archivo productos"),
                                "*.csv", tr("Excel ( *.csv *.xls )") );

    if( file.isEmpty( ) )
    {
        return;
    }

    m_products_model.save( file );
}

//------------------------------------------------------------------------------------------------

void MainWindow::on_actionLoad_triggered( )
{
    QString file = QFileDialog::getOpenFileName( this, tr( "Cargar archivo productos" ),
                                "*.csv", tr("Excel ( *.csv *.xls )") );

    if( file.isEmpty( ) )
    {
        return;
    }

    m_products_model.load( file );

    m_shop.set_products( m_products_model.products( ) );
}

//------------------------------------------------------------------------------------------------

void MainWindow::on_add_product_clicked( )
{
    // TODO Refactor this. Make it shorter (functions or subclass QInputDialog)
    bool ok;

    QString product_name = QInputDialog::getText( this,
                                                  tr("Nombre del Producto"),
                                                  tr("Nombre del Producto:"),
                                                  QLineEdit::Normal,
                                                  "Nombre", &ok );
    if ( !ok || product_name.isEmpty( ) ) return;

    QString product_description = QInputDialog::getText( this,
                                                         tr("Descripcion del Producto"),
                                                         tr("Descripcion del Producto:"),
                                                         QLineEdit::Normal,
                                                         "Descripcion", &ok );
    if ( !ok || product_description.isEmpty( ) ) return;


    QString default_proveider_name =
        ( proveider_box->currentText( ) == all_proveiders ? "Proveedor" : proveider_box->currentText( ) );
    QString product_proveider = QInputDialog::getText( this,
                                                       tr("Proveedor del Producto"),
                                                       tr("Proveedor del Producto:"),
                                                       QLineEdit::Normal,
                                                       default_proveider_name, &ok );
    if ( !ok || product_proveider.isEmpty( ) ) return;

    int product_tax_rate = QInputDialog::getInt( this,
                                                 tr("IVA %"),
                                                 tr("IVA %:"),
                                                 4, 0, 100, 1, &ok );

    if ( !ok || product_tax_rate == 0 ) return;

    QString price_type = QInputDialog::getItem( this,
                                               tr("Precio por Unidad/Kilo ?"),
                                               tr("Precio por Unidad/Kilo ?:"),
                                               m_price_types, 0, false, &ok );
    if ( !ok ) return;

    // change Product to use double instead of float? No need for bigger precision
    double product_raw_price = QInputDialog::getDouble( this,
                                                        tr( "Precio for Ecocity" ),
                                                        tr( "Precio a Ecocity en EUR:" ),
                                                        0, 0, 100000, 2, &ok );
    if ( !ok || product_raw_price == 0 )
    {
        QMessageBox::warning( this, "Producto no creado", "No se ha creado ningun producto" );
        return;
    }

    // TODO Refactor get biggest id to get biggest_id_available
    Product new_product( m_products_model.get_biggest_id( ) + 1,
                         product_name,
                         product_description,
                         product_proveider,
                         product_raw_price,
                         Product::to_price_type( price_type ),
                         product_tax_rate );

    m_shop.add_product( new_product );
    m_products_model.reset( m_shop.products( ) );

    update_proveiders_combobox( );

    products_view->scrollToBottom( );
    products_view->setFocus( );
}

//------------------------------------------------------------------------------------------------

void
MainWindow::on_delete_product_clicked( )
{
   QItemSelectionModel* model = products_view->selectionModel( );

   if ( !model->hasSelection( ) )
   {
       QMessageBox::warning( this, "Nada que eliminar", "No se ha borrado nada porque no has seleccionado ningun producto" );
       return;
   }

   QModelIndex proxy_index = products_view->currentIndex( );

   if ( !proxy_index.isValid( ) )
   {
        QMessageBox::warning( this, "Nada que eliminar", "No se ha borrado nada porque el ModelIndex es invalido" );
       return;
   }

   QModelIndex index = products_filter_model->mapToSource( proxy_index );

   Product product = m_products_model.product( index );

   if ( !ok_to_delete( this, "Eliminar Producto?",
                       tr( "Eliminar Producto con id %1 y nombre %2?" ).arg( product.id() ).arg( product.name( ) ) ) )
   {
       return;
   }

   if ( !m_shop.remove_product( product.name(), product.proveider(), product.price_type() ) )
   {
       assert( 0 );
       QMessageBox::warning( this, "Error", "Error: No se pudo borrar el producto. Contacta Raul :(" );
       return;
   }

   m_products_model.reset( m_shop.products( ) );
}

//------------------------------------------------------------------------------------------------

void
MainWindow::update_proveiders_combobox( )
{
    proveider_box->clear( );

    m_proveiders = Helper::get_proveider_names( m_shop.products( ) );

    // Set Proveiders ComboBox
    proveider_box->addItem( all_proveiders );
    for ( int i = 0; i < m_proveiders.size( ); ++i )
    {
        proveider_box->addItem( m_proveiders.at( i ) );
    }
}

//------------------------------------------------------------------------------------------------

Customer
MainWindow::get_customer_from_dialog( )
{
    // Primero get customer
    CustomerInputDialog customer_dialog( m_shop.customers( ), this );

    if ( customer_dialog.exec() == QDialog::Rejected )
    {
        return Customer( );
    }

    QString customer_name = customer_dialog.get_customer_name( );

    // Dialog was rejected
    if ( customer_name.isEmpty( ) )
    {
        return Customer( );
    }

    const Customer customer = m_shop.customer( customer_name );

    return customer;
}

//------------------------------------------------------------------------------------------------

Invoice
MainWindow::get_invoice_from_dialog( const Customer& customer )
{
    // Primero get customer
    InvoiceDialog invoice_dialog( customer, m_shop.products( ), this );

    int code = invoice_dialog.exec();

    Invoice invoice( QDate::currentDate(), customer );

    if ( code == QDialog::Rejected )
    {
        return invoice;
    }

    invoice = invoice_dialog.get_invoice( );

    return invoice;
}

//------------------------------------------------------------------------------------------------

bool
MainWindow::ok_to_delete( QWidget* parent, const QString& title,
                          const QString& text )
{
    QScopedPointer< QMessageBox > messageBox( new QMessageBox( parent ) );

    if ( parent )
    {
        messageBox->setWindowModality( Qt::WindowModal );
    }

    messageBox->setIcon( QMessageBox::Question );
    messageBox->setWindowTitle( QString( "%1 - %2" )
            .arg( QApplication::applicationName() ).arg( title ) );

    messageBox->setText( text );

    QAbstractButton *deleteButton = messageBox->addButton(
            QObject::tr( "&Eliminar" ), QMessageBox::AcceptRole );

    messageBox->addButton( QObject::tr( "&No Eliminar" ), QMessageBox::RejectRole );
    messageBox->setDefaultButton(
            qobject_cast<QPushButton*>( deleteButton ) );

    messageBox->exec();

    return messageBox->clickedButton() == deleteButton;
}

//------------------------------------------------------------------------------------------------

void MainWindow::on_add_customer_clicked( )
{
    // TODO Refactor this. Make it shorter (functions or subclass QInputDialog)
    bool ok;

    QString name = QInputDialog::getText( this,
                                          tr("Nombre del Cliente"),
                                          tr("Nombre del Cliente:"),
                                          QLineEdit::Normal,
                                          "Nombre del Cliente", &ok );
    if ( !ok || name.isEmpty( ) ) return;

    QString address = QInputDialog::getText( this,
                                             tr("Direccion del Cliente"),
                                             tr("Direccion del Cliente:"),
                                             QLineEdit::Normal,
                                             "Direccion del Cliente", &ok );
    if ( !ok || address.isEmpty( ) ) return;

    QString phone = QInputDialog::getText( this,
                                           tr("Telefono del Cliente"),
                                           tr("Telefono del Cliente:"),
                                           QLineEdit::Normal,
                                           "Telefono del Cliente", &ok );
    if ( !ok || phone.isEmpty( ) ) return;

    QString nif = QInputDialog::getText( this,
                                           tr("NIF del Cliente"),
                                           tr("NIF del Cliente:"),
                                           QLineEdit::Normal,
                                           "NIF del Cliente", &ok );
    if ( !ok || nif.isEmpty( ) ) return;

    QString customer_type = QInputDialog::getItem( this,
                                                   tr("Tipo de Cliente"),
                                                   tr("Tipo de Cliente:"),
                                                   Shop::customer_types(), 0, false, &ok );
    if ( !ok )
    {
        QMessageBox::warning( this, "Cliente no creado", "No se ha creado ningun cliente" );
        return;
    }

    // TODO Refactor get biggest id to get biggest_id_available
    Customer new_customer( m_customers_model.get_biggest_id( ) + 1,
                           name,
                           address,
                           phone,
                           nif,
                           Customer::to_customer_type( customer_type ) );

    m_shop.add_customer( new_customer );
    m_customers_model.reset( m_shop.customers( ) );

    customers_view->scrollToBottom( );
    customers_view->setFocus( );
}

//------------------------------------------------------------------------------------------------

void MainWindow::on_delete_customer_clicked( )
{
    QItemSelectionModel* model = customers_view->selectionModel( );

    if ( !model->hasSelection( ) )
    {
        QMessageBox::warning( this, "Nada que eliminar", "No se ha borrado nada porque no has seleccionado ningun cliente" );
        return;
    }

    QModelIndex proxy_index = customers_view->currentIndex( );

    if ( !proxy_index.isValid( ) )
    {
         QMessageBox::warning( this, "Nada que eliminar", "No se ha borrado nada porque el ModelIndex es invalido" );
        return;
    }

    QModelIndex index = customers_filter_model->mapToSource( proxy_index );

    Customer customer = m_customers_model.customer( index );

    if ( !ok_to_delete( this, "Eliminar Cliente?",
                        tr( "Eliminar Cliente con nombre %1?" ).arg(
                             customer.name( ) ) ) )
    {
        return;
    }

    if( !m_shop.remove_customer( customer.name(), customer.address(), customer.type() ) )
    {
        assert( 0 );
        QMessageBox::warning( this, "Error", "Error: No se pudo borrar el cliente. Contacta Raul :(" );
        return;
    }

    m_customers_model.reset( m_shop.customers( ) );
}

//------------------------------------------------------------------------------------------------

void
MainWindow::show_products_from_proveider( QString proveider_name )
{
    products_filter_model->setFilterKeyColumn( ProductsModel::PRODUCT_PROVIDER );

    if( proveider_name == all_proveiders )
    {
        proveider_name = "";
    }

    products_filter_model->setFilterRegExp( proveider_name );
}

//------------------------------------------------------------------------------------------------

void
MainWindow::show_customer_type( QString customer_type )
{
    customers_filter_model->setFilterKeyColumn( CustomersModel::CUSTOMER_PRICE_TYPE );

    if( customer_type == all_customers )
    {
        customer_type = "";
    }

    customers_filter_model->setFilterRegExp( customer_type );
}

//------------------------------------------------------------------------------------------------

void
MainWindow::update_product_price( QAbstractItemModel * model,
                                  const QModelIndex &index,
                                  float /*price*/ )
{
    // Models are automaticly updated (Source and SortFilterProxy models)
    // Eventhough, the m_shop has to be updated

    QSortFilterProxyModel* products_proxy_model = qobject_cast<QSortFilterProxyModel* >(model);

    // The model I receive is the products_filter_model
    // since the views are set to use this model
    assert( products_proxy_model == products_filter_model );

    QModelIndex price_index_in_source_model = products_filter_model->mapToSource( index );

    Product updated_product = m_products_model.product( price_index_in_source_model );

    // Suposes id never changes!!
    assert( m_shop.update_product( updated_product ) == true );

    m_products_model.reset( m_shop.products( ) );
}

//------------------------------------------------------------------------------------------------

void
MainWindow::on_new_invoice_clicked( )
{
    tabWidget->setCurrentWidget( customers_tab );
    Customer customer = get_customer_from_dialog( );

    if( customer.id( ) == -1 )
    {
        return;
    }

    tabWidget->setCurrentWidget( products_tab );
    Invoice invoice = get_invoice_from_dialog( customer );

    // Dialog was rejected
    if( invoice.items().empty( ) )
    {
        return;
    }

    tabWidget->setCurrentWidget( invoices_tab );
    m_shop.add_invoice( invoice );
    m_shop_model.reset( m_shop );

    InvoicesParser::append_invoice( invoice,
                                    QDir::currentPath( ) + "/csv/writed_invoices.csv" );
}

//------------------------------------------------------------------------------------------------

void MainWindow::on_delete_invoice_clicked( )
{
    QItemSelectionModel* model = invoices_tree_view->selectionModel( );

    if ( !model->hasSelection( ) )
    {
        QMessageBox::warning( this, "Nada que eliminar", "No se ha borrado nada porque no has seleccionado ninguna factura" );
        return;
    }

    QModelIndex index = invoices_tree_view->currentIndex( );

    if ( !index.isValid( ) )
    {
        QMessageBox::warning( this, "Nada que eliminar", "No se ha borrado nada porque el ModelIndex es invalido" );
        return;
    }

    const Invoice* invoice = m_shop_model.invoice( index );

    if ( !invoice )
    {
        QMessageBox::warning( this, "Nada eliminado", "Para borrar una factura, selecciona su fecha y pulsa eliminar factura" );
        return;
    }

    if ( !ok_to_delete( this, "Eliminar Factura?",
                        tr( "Eliminar Factura con fecha %1 de %2?" ).arg(
                            invoice->date().toString( "ddMMyyyy") ).arg(
                            invoice->customer().name() ) ) )
    {
        return;
    }

    if ( !m_shop.remove_invoice( invoice->customer().name(),
                                 invoice->date(),
                                 invoice->total_cost(),
                                 invoice->profit() ) )
    {
        assert( 0 );
        QMessageBox::warning( this, "Error", "Error: No se pudo borrar la factura. Contacta Raul :(" );
        return;
    }

    m_shop_model.reset( m_shop );
}
